//Библиотека Qt.
#include <QFile>
#include <QTreeWidgetItem>
#include <QSettings>


//Внутренняя библиотека.
#include "devmanager.h"
#include "v4l2.h"
#include "filereader.h"
#include "treedeviceitem.h"
#include "threadblocker.h"


//===========================================================================//
//=Заголовок: DevManager::DevManager() - public
//=Параметры:
//=Описание: Конструктор.
//===========================================================================//
DevManager::DevManager(QObject *parent) : QObject(parent)
{qRegisterMetaType<DevParam::SET_PARAM>();}


//===========================================================================//
//=Заголовок: DevManager::~DevManager() - public
//=Параметры:
//=Описание: Деструктор.
//===========================================================================//
DevManager::~DevManager()
{
//Отключить и удалить устройства.
foreach(VidDevice *viddev, vidDevList)
	{
	if(!viddev) continue;
	viddev->quit();
	while(viddev->isRunning()) {}
	delete viddev;
}
}


//===========================================================================//
//=Заголовок: void DevManager::saveConfiguration() - public
//=Параметры:
//=Описание: Функция сохранения настроек приложения.
//===========================================================================//
void DevManager::saveConfiguration() {saveConfiguration(vidDevList);}


//===========================================================================//
//=Заголовок: void DevManager::saveConfiguration() - public
//=Параметры:
//=Описание: Функция сохранения настроек приложения.
//===========================================================================//
void DevManager::saveConfiguration(QList<VidDevice *> viddevlist)
{
QSettings settings;

foreach(VidDevice *viddev, viddevlist)
	{
	if(!viddev) continue;

	settings.beginGroup(QString("DEVICE") + QString::number(viddev->getIndex()));

	//Сохранить тип устройства.
	settings.setValue("TYPE", VidDevice::typeToString(viddev->getType()));

	//Сохранить параметры устройства в зависимости от его типа.
	switch(viddev->getType())
		{
		case VidDevice::VIDEO4LINUX2: {
			//Сохранить базовое имя устройства.
			settings.setValue("BASENAME", viddev->getBaseName());

			//Сохранить имя, имя драйвера и имя файла устройства.
			QVariant devname, drvname, filename;
			viddev->getParam(DevParam::GET_DEVNAME, &devname);
			viddev->getParam(DevParam::GET_DRIVERNAME, &drvname);
			viddev->getParam(DevParam::GET_FILENAME, &filename);
			settings.setValue("DEVNAME", devname);
			settings.setValue("DRIVERNAME", drvname);
			settings.setValue("FILENAME", filename);

			//Сохранить значения видеовхода, видеостандарта, видеоформата и видеоразрешения.
			QVariant input, standard, pixformat, resolution;
			viddev->getParam(DevParam::GET_INPUT, &input);
			viddev->getParam(DevParam::GET_STANDARD, &standard);
			viddev->getParam(DevParam::GET_PIXFORMAT, &pixformat);
			viddev->getParam(DevParam::GET_RESOLUTION, &resolution);
			settings.setValue("INPUT", input);
			settings.setValue("STANDARD", standard);
			settings.setValue("PIXFORMAT", pixformat);
			settings.setValue("RESOLUTION", resolution);

			//Сохранить значения кадрового периода и кэша.
			QVariant maxframerate, framescache;
			viddev->getParam(DevParam::GET_MAX_FRAMERATE, &maxframerate);
			viddev->getParam(DevParam::GET_FRAMESCACHE, &framescache);
			settings.setValue("MAX_FRAMERATE", maxframerate);
			settings.setValue("FRAMESCACHE", framescache);

			QHash<QString, QVariant> varhash;

			//Сохранить значения яркости, контрастности, насыщенности и цветового баланса.
			QVariant brightness;
			viddev->getParam(DevParam::GET_BRIGHTNESS, &brightness);
			varhash = brightness.toHash();
			settings.setValue("BRIGHTNESS", varhash.value("val", 0));

			QVariant contrast;
			viddev->getParam(DevParam::GET_CONTRAST, &contrast);
			varhash = contrast.toHash();
			settings.setValue("CONTRAST", varhash.value("val", 0));

			QVariant hue;
			viddev->getParam(DevParam::GET_HUE, &hue);
			varhash = hue.toHash();
			settings.setValue("HUE", varhash.value("val", 0));

			QVariant saturation;
			viddev->getParam(DevParam::GET_SATURATION, &saturation);
			varhash = saturation.toHash();
			settings.setValue("SATURATION", varhash.value("val", 0));

			//Сохранить значение параметра, определяющего создание видеоокна.
			QVariant devwin_connected;
			viddev->getParam(DevParam::GET_VIDEOWINDOW_CONNECTED, &devwin_connected);
			settings.setValue("VIDEOWINDOW_CONNECTED", devwin_connected);

			//Сохранить значения параметров детектора движения.
			QVariant motion_enabled, motion_treshold, motion_sensitive, motion_noiseremoving;
			viddev->getParam(DevParam::GET_MOTION_ENABLED, &motion_enabled);
			viddev->getParam(DevParam::GET_MOTION_TRESHOLD, &motion_treshold);
			viddev->getParam(DevParam::GET_MOTION_SENSITIVE, &motion_sensitive);
			viddev->getParam(DevParam::GET_MOTION_NOISEREMOVING, &motion_noiseremoving);
			settings.setValue("MOTION_ENABLED", motion_enabled);
			settings.setValue("MOTION_TRESHOLD", motion_treshold);
			settings.setValue("MOTION_SENSITIVE", motion_sensitive);
			settings.setValue("MOTION_NOISEREMOVING", motion_noiseremoving);

			//Сохранить значения параметров записи в файл.
			QVariant filewriter_enabled;
			viddev->getParam(DevParam::GET_FILEWRITER_ENABLED, &filewriter_enabled);
			settings.setValue("FILEWRITER_ENABLED", filewriter_enabled);
		} break;
		case VidDevice::FILEREADER: break;
	}

	settings.endGroup();
}
}


//===========================================================================//
//=Заголовок: void DevManager::loadConfiguration() - public
//=Параметры:
//=Описание: Функция загрузки настроек приложения.
//===========================================================================//
void DevManager::loadConfiguration(QStringList viddevstrlist)
{
QSettings settings;

//Создать локальный список вновь подключенных устройств.
QList<VidDevice *> viddevlist;

foreach(QString viddevstr, viddevstrlist)
	{
	settings.beginGroup(viddevstr);

	//Получить тип устройства.
	QString typestr = settings.value("TYPE", "").toString();
	if(typestr.isEmpty()) {settings.endGroup(); continue;}

	//Создать новое устройство в зависимости от полученного типа.
	if(typestr == "VIDEO4LINUX2")
		{
		//Получить имя файла устройства.
		QString devfname;
		devfname = settings.value("FILENAME", "").toString();
		if(devfname.isEmpty()) {settings.endGroup(); continue;}

		//Проверить, что указанное устройство не присутствует в списке устройств.
		VidDevice *viddev = findDeviceFromFileName(devfname);
		if(viddev)
			{
			QString msg = tr("попытка повторного подключения существующего видеоустройства");
			emit warning(msg); settings.endGroup(); continue;
		}

		//Найти первый свободный индекс для вновь подключаемого устройства.
		int index = 0; while(getDevice(index)) index++;

		//Создать новое устройство.
		viddev = new V4L2(devfname, index);
		connect(viddev, SIGNAL(information(const QString &)), this, SIGNAL(information(const QString &)), Qt::QueuedConnection);
		connect(viddev, SIGNAL(warning(const QString &)), this, SIGNAL(warning(const QString &)), Qt::QueuedConnection);
		connect(viddev, SIGNAL(critical(const QString &)), this, SIGNAL(critical(const QString &)), Qt::QueuedConnection);

		//Установить базовое имя устройства.
		QString devbname = settings.value("BASENAME", "").toString();
		if(!devbname.isEmpty()) viddev->setBaseName(devbname);

		//Добавить устройство в блокировщик.
		ThreadBlocker blocker(this);
		blocker.addObject(index, viddev);

		//Активировать устройство.
		viddev->start(QThread::HighestPriority);

		//Дождаться исполнения комманды.
		blocker.block();

		//Проверить соответствие сохранённого имени устройства имени реального устройства.
		QVariant devname;
		viddev->getParam(DevParam::GET_DEVNAME, &devname);
		if(devname != settings.value("DEVNAME", ""))
			{
			viddev->quit(); while(viddev->isRunning()) {}
			delete viddev; settings.endGroup(); continue;
		}

		//Проверить соответствие сохранённого имени драйвера устройства имени драйвера реального устройства.
		QVariant drvname;
		viddev->getParam(DevParam::GET_DRIVERNAME, &drvname);
		if(drvname != settings.value("DRIVERNAME", ""))
			{
			viddev->quit(); while(viddev->isRunning()) {}
			delete viddev; settings.endGroup(); continue;
		}

		//Добавить устройство в глобальный список.
		vidDevList.append(viddev);

		//Выбрать устройство.
		selectDevice(viddev);

		//Установить значения видеовхода, видеостандарта, видеоформата и видеоразрешения.
		int input = settings.value("INPUT", -1).toInt();
		if(input != -1) setParam(DevParam::SET_INPUT, input);
		int standard = settings.value("STANDARD", -1).toInt();
		if(standard != -1) setParam(DevParam::SET_STANDARD, standard);
		int pixformat = settings.value("PIXFORMAT", -1).toInt();
		if(pixformat != -1) setParam(DevParam::SET_PIXFORMAT, pixformat);
		int resolution = settings.value("RESOLUTION", -1).toInt();
		if(resolution != -1) setParam(DevParam::SET_RESOLUTION, resolution);

		//Установить значения кадрового периода и кэша.
		int maxframerate = settings.value("MAX_FRAMERATE", -1).toInt();
		if(maxframerate != -1) setParam(DevParam::SET_MAX_FRAMERATE, maxframerate);
		int framescache = settings.value("FRAMESCACHE", -1).toInt();
		if(framescache != -1) setParam(DevParam::SET_FRAMESCACHE, framescache);

		//Установить значения яркости, контрастности, насыщенности и цветового баланса.
		int brightness = settings.value("BRIGHTNESS", 0).toInt();
		if(brightness != 0) setParam(DevParam::SET_BRIGHTNESS, brightness);
		int contrast = settings.value("CONTRAST", 0).toInt();
		if(contrast != 0) setParam(DevParam::SET_CONTRAST, contrast);
		int hue = settings.value("HUE", 0).toInt();
		if(hue != 0) setParam(DevParam::SET_HUE, hue);
		int saturation = settings.value("SATURATION", 0).toInt();
		if(saturation != 0) setParam(DevParam::SET_SATURATION, saturation);

		//Получить значение параметра, определяющего создание видеоокна.
		bool devwin_connected = settings.value("VIDEOWINDOW_CONNECTED", true).toBool();
		if(devwin_connected) viddevlist.append(viddev);

		//Установить значения параметров детектора движения.
		int motion_treshold = settings.value("MOTION_TRESHOLD", -1).toInt();
		if(motion_treshold != -1) setParam(DevParam::SET_MOTION_TRESHOLD, motion_treshold);
		int motion_sensitive = settings.value("MOTION_SENSITIVE", -1).toInt();
		if(motion_sensitive != -1) setParam(DevParam::SET_MOTION_SENSITIVE, motion_sensitive);
		setParam(DevParam::SET_MOTION_NOISEREMOVING, settings.value("MOTION_NOISEREMOVING", false).toBool());
		setParam(DevParam::SET_MOTION_ENABLED, settings.value("MOTION_ENABLED", false).toBool());

		//Установить значения параметров записи в файл.
		bool filewriter_enabled = settings.value("FILEWRITER_ENABLED", false).toBool();
		if(filewriter_enabled) setParam(DevParam::SET_FILEWRITER_TRIGGER, true);
	}
	else if(typestr == "FILEREADER") {}

	settings.endGroup();
}

//Отправить сигнал о добавлении в программу новых устройств.
emit devicesConnect(viddevlist);
}


//===========================================================================//
//=Заголовок: void DevManager::loadDevicesFromSystem() - public
//=Параметры:
//=Описание: Функция загрузки видеоустройств из системы.
//===========================================================================//
void DevManager::loadDevicesFromSystem()
{
ThreadBlocker blocker(this);

//Создать локальный список вновь подключенных устройств.
QList<VidDevice *> viddevlist;

//Получить список устройств, доступных в системе.
int devfindex = 0;
QString devfname = QString("/dev/video") + QString::number(devfindex);
while(QFile::exists(devfname))
	{
	//Проверить, что указанное устройство не присутствует в списке устройств.
	VidDevice *viddev = findDeviceFromFileName(devfname);
	if(viddev)
		{
		//Перейти к следующему файлу устройства.
		devfindex++; devfname = QString("/dev/video") + QString::number(devfindex);
		continue;
	}

	//Найти первый свободный индекс для вновь подключаемого устройства.
	int index = 0; while(getDevice(index)) index++;

	//Создать новое устройство.
	V4L2 *v4l2 = new V4L2(devfname, index);
	connect(v4l2, SIGNAL(information(const QString &)), this, SIGNAL(information(const QString &)), Qt::QueuedConnection);
	connect(v4l2, SIGNAL(warning(const QString &)), this, SIGNAL(warning(const QString &)), Qt::QueuedConnection);
	connect(v4l2, SIGNAL(critical(const QString &)), this, SIGNAL(critical(const QString &)), Qt::QueuedConnection);

	//Добавить устройство в блокировщик.
	blocker.addObject(index, v4l2);

	//Активировать нить устройства.
	v4l2->start(QThread::HighestPriority);

	//Добавить устройство в глобальный список.
	vidDevList.append(v4l2);

	//Добавить устройство в локальный список.
	viddevlist.append(v4l2);

	//Перейти к следующему файлу устройства.
	devfindex++; devfname = QString("/dev/video") + QString::number(devfindex);
}

//Отправить сигнал о добавлении в программу новых устройств.
blocker.block(); emit devicesConnect(viddevlist);

//Отправить сообщение, если новые устройства не найдены.
if(!viddevlist.isEmpty())
	{
	QString msg = tr("новые видеоустройства не найдены");
	emit information(msg);
}
}


//===========================================================================//
//=Заголовок: bool DevManager::addDevice() - public
//=Параметры:
//=Описание: Функция добавления устройства.
//===========================================================================//
bool DevManager::addDevice(QString devfname)
{
//Проверить, что указанное устройство не присутствует в списке устройств.
VidDevice *viddev = findDeviceFromFileName(devfname);
if(viddev)
	{
	QString msg = tr("указанное видеоустройство уже подключено");
	emit warning(msg); return false;
}

//Найти первый свободный индекс для вновь подключаемого устройства.
int index = 0; while(getDevice(index)) index++;

//Создать и добавить новое устройство в глобальный список.
V4L2 *v4l2 = new V4L2(devfname, index);
connect(v4l2, SIGNAL(information(const QString &)), this, SIGNAL(information(const QString &)), Qt::QueuedConnection);
connect(v4l2, SIGNAL(warning(const QString &)), this, SIGNAL(warning(const QString &)), Qt::QueuedConnection);
connect(v4l2, SIGNAL(critical(const QString &)), this, SIGNAL(critical(const QString &)), Qt::QueuedConnection);
v4l2->start(QThread::HighestPriority);
vidDevList.append(v4l2);

//Добавить новое устройство в локальный список.
QList<VidDevice *> viddevlist;
viddevlist.append(v4l2);

//Отправить сигнал о добавлении в программу нового устройства.
emit devicesConnect(viddevlist);

return true;
}


//===========================================================================//
//=Заголовок: bool DevManager::addFilePlayer() - public
//=Параметры:
//=Описание: Функция добавления файлового плэйера.
//===========================================================================//
bool DevManager::addFilePlayer(QString path)
{
//Найти первый свободный индекс для вновь подключаемого устройства (плэйера).
int index = 0; while(getDevice(index)) index++;

//Создать новый файловый плэйер и добавить его в глобальный список.
FileReader *filereader = new FileReader(path, index);
connect(filereader, SIGNAL(information(const QString &)), this, SIGNAL(information(const QString &)), Qt::QueuedConnection);
connect(filereader, SIGNAL(warning(const QString &)), this, SIGNAL(warning(const QString &)), Qt::QueuedConnection);
connect(filereader, SIGNAL(critical(const QString &)), this, SIGNAL(critical(const QString &)), Qt::QueuedConnection);
filereader->start(QThread::LowestPriority);
vidDevList.append(filereader);

//Добавить новое устройство в локальный список.
QList<VidDevice *> viddevlist;
viddevlist.append(filereader);

//Отправить сигнал о добавлении в программу нового устройства.
emit devicesConnect(viddevlist);

return true;
}


//===========================================================================//
//=Заголовок: void DevManager::deleteDevices() - public
//=Параметры:
//=Описание: Функция удаления устройств.
//===========================================================================//
void DevManager::deleteDevices(QList<VidDevice *> viddevlist)
{
QList<int> devindexlist;

//Удалить устройства.
VidDevice *viddev = NULL;
foreach(viddev, viddevlist)
	{
	if(!viddev) continue;
	devindexlist.append(viddev->getIndex());
	deleteDevice(viddev->getIndex());
}

//Отправить сигнал о необходимости обновления видеоокон.
emit devicesDisconnect(devindexlist);
}


//===========================================================================//
//=Заголовок: bool DevManager::deleteDevice() - public
//=Параметры:
//=Описание: Функция удаления устройства.
//===========================================================================//
bool DevManager::deleteDevice(int index)
{
//Удалить из списка выбранных устройств устройство с указанным индексом.
VidDevice *viddev = takeDevice(index, &selVidDevList);

//Удалить это же устройство из общего списка устройств.
viddev = takeDevice(index, &vidDevList);
if(!viddev) return false;

//Отключить и удалить устройство.
viddev->quit(); delete viddev;

return true;
}


//===========================================================================//
//=Заголовок: void DevManager::selectAllDevices() - public
//=Параметры:
//=Описание: Функция выбора всех устройств.
//===========================================================================//
void DevManager::selectAllDevices() {selectDevices(vidDevList);}


//===========================================================================//
//=Заголовок: void DevManager::selectDevice() - public
//=Параметры:
//=Описание: Функция выбора одного устройства.
//===========================================================================//
void DevManager::selectDevice(VidDevice *viddev)
{
QList<VidDevice *> viddevlist;
viddevlist.append(viddev);
selectDevices(viddevlist);
}


//===========================================================================//
//=Заголовок: void DevManager::selectDevices() - public
//=Параметры:
//=Описание: Функция выбора устройств.
//===========================================================================//
void DevManager::selectDevices(QList<VidDevice *> viddevlist)
{
//Отключить от функций управления все выбранные на данный момент устройства.
clearDevicesSelection();

//Добавить в список выбранных устройств указанные устройства.
foreach(VidDevice *viddev, viddevlist)
	{
	if(!viddev) continue;
	connect(viddev, SIGNAL(framerateChanged(int)), this, SIGNAL(deviceFramerateChanged(int)), Qt::QueuedConnection);
	connect(this, SIGNAL(sigSetParam(DevParam::SET_PARAM, int)), viddev, SLOT(slotSetParam(DevParam::SET_PARAM, int)), Qt::QueuedConnection);
	connect(this, SIGNAL(sigSetParam(DevParam::SET_PARAM, bool)), viddev, SLOT(slotSetParam(DevParam::SET_PARAM, bool)), Qt::QueuedConnection);
	selVidDevList.append(viddev);
}

//Отправить сигнал о выборе новых устройств.
emit devicesSelected();
}


//===========================================================================//
//=Заголовок: void DevManager::clearDevicesSelection() - public
//=Параметры:
//=Описание: Функция отмены выбора устройств.
//===========================================================================//
void DevManager::clearDevicesSelection()
{
//Отключить от функций управления все выбранные на данный момент устройства.
foreach(VidDevice *viddev, selVidDevList)
	{
	if(!viddev) continue;
	disconnect(viddev, SIGNAL(framerateChanged(int)), this, SIGNAL(deviceFramerateChanged(int)));
	disconnect(this, SIGNAL(sigSetParam(DevParam::SET_PARAM, int)), viddev, SLOT(slotSetParam(DevParam::SET_PARAM, int)));
	disconnect(this, SIGNAL(sigSetParam(DevParam::SET_PARAM, bool)), viddev, SLOT(slotSetParam(DevParam::SET_PARAM, bool)));
}

//Очистить список выбранных устройств.
selVidDevList.clear();
}


//===========================================================================//
//=Заголовок: VidDevice *DevManager::getDevice() - public
//=Параметры:
//=Описание: Функция возврата видеоустройства.
//===========================================================================//
VidDevice *DevManager::getDevice(int index)
{
VidDevice *viddev = NULL;
foreach(viddev, vidDevList)
	{
	if(!viddev) continue;
	if(viddev->getIndex() == index) return viddev;
}

return NULL;
}


//===========================================================================//
//=Заголовок: VidDevice *DevManager::findDeviceFromFileName() - public
//=Параметры:
//=Описание: Функция поиска устройства в списке по имени файла.
//===========================================================================//
VidDevice *DevManager::findDeviceFromFileName(QString devfname)
{
VidDevice *viddev = NULL;
foreach(viddev, vidDevList)
	{
	if(!viddev) continue;
	QVariant cur_devfname;
	viddev->getParam(DevParam::GET_FILENAME, &cur_devfname);
	if(devfname == cur_devfname.toString()) return viddev;
}

return NULL;
}


//===========================================================================//
//=Заголовок: QList<QTreeWidgetItem *> DevManager::getDevicesGroupList() - public
//=Параметры:
//=Описание: Функция возврата списка устройств.
//===========================================================================//
QList<QTreeWidgetItem *> DevManager::getDevicesGroupList(int dev_col, int win_col, int motion_col, int /*net_col*/)
{
QList<QTreeWidgetItem *> devitemslist;
QHash<TreeDeviceItem *, QStringList> groupshash;

QFont font; font.setBold(true);

//Получить параметры устройств.
for(int i = 0; i < vidDevList.size(); ++i)
	{
	VidDevice *viddev = vidDevList.at(i);
	if(!viddev) continue;

	//Получить наименование устройства.
	QString fname = viddev->getBaseName();

	//Получить основные параметры устройства.
	QVariant devname, devfname;
	viddev->getParam(DevParam::GET_DEVNAME, &devname);
	viddev->getParam(DevParam::GET_FILENAME, &devfname);

	//Получить список параметров устройства.
	QStringList devparamslist = getDeviceParams(viddev);

	//Выполнить поиск соответствия полученного списка к конкретной группе устройств.
	TreeDeviceItem *groupitem = NULL;
	QHashIterator<TreeDeviceItem *, QStringList> groupshash_i(groupshash);
	while(groupshash_i.hasNext())
		{
		groupshash_i.next();
		if(isEqualLists(groupshash_i.value(), devparamslist))
			{groupitem = groupshash_i.key(); break;}
	}

	//Определить изображения (иконки) устройства и группы устройств в визуальном списке.
	QString deviconstr, groupiconstr;
	switch(viddev->getType())
		{
		case VidDevice::VIDEO4LINUX2:
			deviconstr = ":/images/vidcamera.png";
			groupiconstr = ":/images/groupdev.png";
		break;
		case VidDevice::FILEREADER:
			groupiconstr = deviconstr = ":/images/player.png";
		break;
	}

	//Создать представление устройства в визуальном списке.
	TreeDeviceItem *childitem = new TreeDeviceItem(fname);
	switch(viddev->getType())
		{
		case VidDevice::VIDEO4LINUX2: childitem->setIcon(dev_col, QIcon(":/images/vidcamera.png")); break;
		case VidDevice::FILEREADER: childitem->setIcon(dev_col, QIcon(":/images/player.png")); break;
	}
	childitem->setIcon(dev_col, QIcon(deviconstr));
	childitem->setSizeHint(dev_col, QSize(childitem->sizeHint(dev_col).width(), 20));
	childitem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable);
	childitem->addDevice(viddev);

	//Определить, подключено ли к устройству видеоокно.
	QVariant devwinconnected;
	viddev->getParam(DevParam::GET_VIDEOWINDOW_CONNECTED, &devwinconnected);
	if(devwinconnected.toBool()) childitem->setCheckState(win_col, Qt::Checked);
	else childitem->setCheckState(win_col, Qt::Unchecked);

	//Определить, подключен ли к устройству фильтр детектора движения.
	QVariant motionconnected;
	viddev->getParam(DevParam::GET_MOTION_CONNECTED, &motionconnected);
	if(motionconnected.toBool()) childitem->setCheckState(motion_col, Qt::Checked);
	else childitem->setCheckState(motion_col, Qt::Unchecked);

	//Если устройство полностью соответствует группе.
	if(groupitem)
		{
		groupitem->addChild(childitem);
		groupitem->addDevice(viddev);
	}
	else {
		groupitem = new TreeDeviceItem(devname.toString());
		groupitem->setIcon(dev_col, QIcon(groupiconstr));
		groupitem->setFont(dev_col, font);
		groupitem->addChild(childitem);
		groupitem->addDevice(viddev);

		groupshash.insert(groupitem, devparamslist);
		devitemslist.append(groupitem);
	}
}

return devitemslist;
}


//===========================================================================//
//=Заголовок: void DevManager::getListParam() - public
//=Параметры:
//=Описание: Функция возврата списка значений параметра выбранных устройств.
//===========================================================================//
void DevManager::getListParam(DevParam::GET_LIST_PARAM param, QHash<int, QVariant> *valhash)
{
QHash<int, QVariant> hash;

VidDevice *viddev = NULL;
foreach(viddev, selVidDevList)
	{
	if(!viddev) continue;

	QHash<int, QVariant> curhash;
	viddev->getListParam(param, &curhash);
	if(hash.isEmpty()) {hash = curhash; continue;}

	QHashIterator<int, QVariant> i(hash);
	while(i.hasNext())
		{
		i.next();
		if(!curhash.contains(i.key()))
			{hash.remove(i.key()); continue;}
		if(curhash.value(i.key()) != i.value())
			hash.remove(i.key());
	}
}

(*valhash) = hash;
}


//===========================================================================//
//=Заголовок: void DevManager::getParam() - public
//=Параметры:
//=Описание: Функция возврата значения параметра выбранных устройств.
//===========================================================================//
void DevManager::getParam(DevParam::GET_PARAM param, QVariant *value)
{
QVariant var;

VidDevice *viddev = NULL;
foreach(viddev, selVidDevList)
	{
	if(!viddev) continue;

	QVariant curvar;
	viddev->getParam(param, &curvar);
	if(var.isNull()) {var = curvar; continue;}
	if(var != curvar) {(*value) = -1; return;}
}

(*value) = var;
}


//===========================================================================//
//=Заголовок: void DevManager::setParam() - public
//=Параметры:
//=Описание: Функция установки значения параметра выбранных устройств.
//===========================================================================//
void DevManager::setParam(DevParam::SET_PARAM param, QVariant value)
{
ThreadBlocker blocker(this);
foreach(VidDevice *viddev, selVidDevList)
	{
	if(!viddev) continue;
	blocker.addObject(viddev->getIndex(), viddev);
}

switch(value.type())
	{
	case QVariant::Bool: emit sigSetParam(param, value.toBool()); break;
	case QVariant::Int:  emit sigSetParam(param, value.toInt()); break;
	default: return;
}

blocker.block();
}


//===========================================================================//
//=Заголовок: void DevManager::setMotionEnabled() - public
//=Параметры:
//=Описание: Функция управления активностью детектора движения.
//===========================================================================//
void DevManager::setMotionEnabled(bool enabled)
{emit sigSetParam(DevParam::SET_MOTION_ENABLED, enabled);}


//===========================================================================//
//=Заголовок: void DevManager::setFileWriterTrigger() - public
//=Параметры:
//=Описание: Функция управления активностью записи видео в файл.
//===========================================================================//
void DevManager::setFileWriterTrigger(bool is_absolute)
{emit sigSetParam(DevParam::SET_FILEWRITER_TRIGGER, is_absolute);}


//===========================================================================//
//=Заголовок: VidDevice *DevManager::takeDevice() - private
//=Параметры:
//=Описание: Функция возврата устройства с удалением его из списка.
//===========================================================================//
VidDevice *DevManager::takeDevice(int index, QList<VidDevice *> *viddevlist)
{
if(!viddevlist) return NULL;

for(int i = 0; i < viddevlist->size(); ++i)
	{
	VidDevice *viddev = viddevlist->at(i);
	if(!viddev) continue;

	if(viddev->getIndex() == index)
		{return viddevlist->takeAt(i);}
}

return NULL;
}


//===========================================================================//
//=Заголовок: QStringList DevManager::getDeviceParams() - private
//=Параметры:
//=Описание: Функция возврата списка параметров устройства.
//===========================================================================//
QStringList DevManager::getDeviceParams(VidDevice *viddev)
{
//Получить параметры устройства.
QHash<int, QVariant> inputshash, standardshash, formatshash, reshash;
viddev->getListParam(DevParam::GET_LIST_INPUTS, &inputshash);
viddev->getListParam(DevParam::GET_LIST_STANDARDS, &standardshash);
viddev->getListParam(DevParam::GET_LIST_PIXFORMATS, &formatshash);
viddev->getListParam(DevParam::GET_LIST_RESOLUTIONS, &reshash);

//Сформировать список параметров устройства.
QStringList devparamslist;
devparamslist << VidDevice::typeToString(viddev->getType());
foreach(QVariant value, inputshash) devparamslist << value.toStringList();
foreach(QVariant value, standardshash) devparamslist << value.toStringList();
foreach(QVariant value, formatshash) devparamslist << value.toStringList();
foreach(QVariant value, reshash) devparamslist << value.toStringList();

return devparamslist;
}


//===========================================================================//
//=Заголовок: bool DevManager::isEqualLists() - private
//=Параметры:
//=Описание: Функция возврата эквивалентности двух списков.
//===========================================================================//
bool DevManager::isEqualLists(QStringList firstlist, QStringList secondlist)
{
if(firstlist.size() < secondlist.size()) return false;
if(firstlist.size() > secondlist.size()) return false;

bool equal = true;
for(int ii = 0; ii < firstlist.size(); ++ii)
	{
	if(firstlist.at(ii) != secondlist.at(ii))
		{equal = false; break;}
}

return equal;
}
